
import java.util.Iterator;
import java.util.Set;
import java.util.ArrayList;

public class Node {
	Integer myvid;
	/*
	 * virtual id of the node
	 */
	Integer myuid;
	/*
	 * unique id of the node
	 */
	public Set<Integer> myPhyNeighbors;
	/*
	 * Its a list of double values, which contains the neighbors for this particular node
	 */
	public ArrayList<rtEntry> RoutingTable;
	/*
	 * Routing Table entries 
	 */
	public World myworld;
	public int leftneighbor;
	public int rightneighbor;
	/*
	 * Handle to world class
	 */
	public Node(Integer uid, Integer vid, Set<Integer> neighbors, World w){

		myuid = uid;
		myvid = vid;
		myPhyNeighbors = neighbors;
		RoutingTable = new ArrayList<rtEntry>();
		myworld = w;
	}

	public void addRtEntry(int nh_vid, int id, int end1, int end2, int t){

		RoutingTable.add(new rtEntry(nh_vid, id, end1, end2, t));
	}

	public void buildRoutingTable(){

		int otherVid;
		int currentNode;
		int nexthopright, nexthopleft; 
		// determine the logical neighbors here
		// THIS CODE SHOULD BE MOVED SOMEWHERE ELSE
		// CHECK THIS LOGIC... IF ELSE CONSTRAINT SHOULD BE CORRECTED
		// rightneighbor and leftnrighbor mistake correct in actual code
		// WRONG CHECK IT CHANGE COUNTER IN WHILE LOOP
		rightneighbor = (myvid +  1) % (myworld.nNodes);
		nexthopright = myworld.getOSPFnextHop(myvid, rightneighbor);
		int maxsize = myworld.nNodes - 1;   // maxsize should be number of nodes in the network
		while (nexthopright == -1 && maxsize > 0){
			maxsize--;
			rightneighbor = (rightneighbor+1 ) % (myworld.nNodes);
			nexthopright = myworld.getOSPFnextHop(myvid, rightneighbor);
		}
		if (maxsize == 0){
			World.println("Warning: Node "+ myvid + " uid = " + myuid + " couldn't find it's right logical neighbor!!", false);
		}
		World.println("Found the right left Neighbors: "+rightneighbor, false);
		leftneighbor = (myvid +myworld.nNodes -  1) % (myworld.nNodes);
		nexthopleft = myworld.getOSPFnextHop(myvid, leftneighbor);
		maxsize = myworld.nNodes; // MAXSIZE SHOULD BE NUMBER OF NODES IN THE NETWORK
		while (nexthopleft == -1 && maxsize > 0){
			maxsize--;
			leftneighbor = (leftneighbor + myworld.nNodes -  1) % (myworld.nNodes);
			nexthopleft = myworld.getOSPFnextHop(myvid, leftneighbor);
		}
		if (maxsize == 0){
			World.println("Warning: Node "+ myvid + " uid = " + myuid + " couldn't find it's right logical neighbor!!", false);
		}
		World.println("Found the right left Neighbors: "+rightneighbor+ " left is: "+ leftneighbor + "nextHop found is "+ nexthopright + " lefthop" + nexthopleft, false);
		Node interNode;
		ArrayList<Integer> neighbors = getNeighbors();
		for (int i = 0; i< neighbors.size(); i++){
			otherVid = neighbors.get(i);
			rtEntry newrtEntry = new rtEntry();
			newrtEntry.end1 = myvid;
			newrtEntry.end2 = otherVid;
			newrtEntry.pathId = myworld.getPathId(myvid, otherVid);
			newrtEntry.nextHop = myworld.getOSPFnextHop(myvid, otherVid);
			World.println("Calling getOSPFNextHop with these values: "+myvid+", "+otherVid,false);
			newrtEntry.type = rtEntry.EMPTY;
			RoutingTable.add(newrtEntry);
			currentNode = newrtEntry.nextHop;

			while (currentNode != newrtEntry.end2){
				rtEntry newrtEntry1 = new rtEntry();
				newrtEntry1.pathId = newrtEntry.pathId;
				newrtEntry1.end1 = newrtEntry.end1;
				newrtEntry1.end2 = newrtEntry.end2;
				World.println("Inside while loop: Calling getOSPFNextHop with these values: "+currentNode+", "+otherVid,false);
				newrtEntry1.nextHop = myworld.getOSPFnextHop(currentNode, otherVid);
				newrtEntry1.type = rtEntry.EMPTY;
				interNode = myworld.node.get(currentNode);
				if (interNode != null)
					interNode.RoutingTable.add(newrtEntry1);
				currentNode = newrtEntry1.nextHop;	
			}        	
		}
	}

	public ArrayList<Integer> getNeighbors(){
		ArrayList<Integer> myNeighbors = new ArrayList<Integer>();
		for (Iterator<Integer> vidit = myPhyNeighbors.iterator(); vidit.hasNext();){
			int vid = vidit.next();
			if (! myNeighbors.contains(vid)){
				myNeighbors.add(vid);
			}    	
		}
		myNeighbors.add(leftneighbor);
		myNeighbors.add(rightneighbor);
		return myNeighbors;
	}    

	/*
	 * CONFUSION: WHAT TO DO IN CASE OF PATHID NOT PRESENT.
	 * WHAT TO DO IF DESTVID NOT PRESENT
	 * WHEN MESSAGE.PID WILL BE -1;
	 */

	public int getNextHop(Message msg){

		return lookupRoutingTable(msg);

	}

	public int lookupRoutingTable(Message msg){

		rtEntry routingentry;
		for (int i = 0; i< RoutingTable.size(); i++){
			routingentry = RoutingTable.get(i);
			if (msg.destVid == routingentry.end2){
				msg.pathId = routingentry.pathId;
				return routingentry.nextHop;
			}
		}    
		for (int i = 0; i< RoutingTable.size(); i++){
			routingentry = RoutingTable.get(i);
			if (msg.pathId== routingentry.pathId){
				return routingentry.nextHop;
			}
		}  

		// else send it to the closer neighbor
		int dist2rightNeighbor = (msg.destVid - rightneighbor + myworld.nNodes)%myworld.nNodes;
		int dist2leftNeighbor = (msg.destVid - leftneighbor + myworld.nNodes)%myworld.nNodes;
		int closerneighbor = (dist2leftNeighbor > dist2rightNeighbor)? rightneighbor:leftneighbor;
		for (int i = 0; i< RoutingTable.size(); i++){
			routingentry = RoutingTable.get(i);
			if (closerneighbor == routingentry.end2){
				msg.pathId = routingentry.pathId;
				return routingentry.nextHop;
			}
		}  
		return -1;
	}    
}
